#include <vector>
#include <cassert>
using namespace std;

/* 二分查找（双闭区间） */
int binarySearch(vector<int> &nums, int target)
{
    // 初始化双闭区间 [0, n-1] ，即 i, j 分别指向数组首元素、尾元素
    int i = 0, j = nums.size() - 1;
    // 循环，当搜索区间为空时跳出（当 i > j 时为空）
    while (i <= j)
    {
        int m = i + (j - i) / 2; // 计算中点索引 m
        if (nums[m] < target)    // 此情况说明 target 在区间 [m+1, j] 中
            i = m + 1;
        else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中
            j = m - 1;
        else // 找到目标元素，返回其索引
            return m;
    }
    // 未找到目标元素，返回 -1
    return -1;
}

/* 二分查找（左闭右开区间） */
int binarySearchLCRO(vector<int> &nums, int target)
{
    // 初始化左闭右开区间 [0, n) ，即 i, j 分别指向数组首元素、尾元素 +1
    int i = 0, j = nums.size();
    // 循环，当搜索区间为空时跳出（当 i = j 时为空）
    while (i < j)
    {
        int m = i + (j - i) / 2; // 计算中点索引 m
        if (nums[m] < target)    // 此情况说明 target 在区间 [m+1, j) 中
            i = m + 1;
        else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中
            j = m;
        else // 找到目标元素，返回其索引
            return m;
    }
    // 未找到目标元素，返回 -1
    return -1;
}

/* 二分查找插入点（无重复元素） */
int binarySearchInsertionSimple(vector<int> &nums, int target)
{
    int i = 0, j = nums.size() - 1; // 初始化双闭区间 [0, n-1]
    while (i <= j)
    {
        int m = i + (j - i) / 2; // 计算中点索引 m
        if (nums[m] < target)
        {
            i = m + 1; // target 在区间 [m+1, j] 中
        }
        else if (nums[m] > target)
        {
            j = m - 1; // target 在区间 [i, m-1] 中
        }
        else
        {
            return m; // 找到 target ，返回插入点 m
        }
    }
    // 未找到 target ，返回插入点 i
    return i;
}

/* 二分查找插入点（存在重复元素） */
int binarySearchInsertion(vector<int> &nums, int target)
{
    int i = 0, j = nums.size() - 1; // 初始化双闭区间 [0, n-1]
    while (i <= j)
    {
        int m = i + (j - i) / 2; // 计算中点索引 m
        if (nums[m] < target)
        {
            i = m + 1; // target 在区间 [m+1, j] 中
        }
        else if (nums[m] > target)
        {
            j = m - 1; // target 在区间 [i, m-1] 中
        }
        else
        {
            j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
        }
    }
    // 返回插入点 i
    return i;
}

/* 二分查找最左一个 target */
int binarySearchLeftEdge(vector<int> &nums, int target)
{
    // 等价于查找 target 的插入点
    int i = binarySearchInsertion(nums, target);
    // 未找到 target ，返回 -1
    if (i == nums.size() || nums[i] != target)
    {
        return -1;
    }
    // 找到 target ，返回索引 i
    return i;
}

int main()
{
    vector list{1, 3, 6, 8, 12, 15, 23, 26, 31, 35};

    int index = binarySearch(list, 12);
    int notFound = binarySearch(list, 20);
    assert(list[index] == 12);
    assert(notFound == -1);
}